package join

import (
	"context"
	"encoding/hex"
	"github.com/pkg/errors"
	"network/v3/internal/config"
	"network/v3/internal/models"
	//"network/v3/internal/roaming"
	"github.com/brocaar/lorawan"
	"github.com/brocaar/lorawan/backend"
	"network/v3/internal/storage"
)

// ErrAbort is used to abort the flow without error
var ErrAbort = errors.New("nothing to do")

type joinContext struct {
	ctx context.Context

	RXPacket           models.RXPacket
	JoinRequestPayload *lorawan.JoinRequestPayload
	Device             storage.Device
	ServiceProfile     storage.ServiceProfile
	DeviceProfile      storage.DeviceProfile
	DevAddr            lorawan.DevAddr
	CFList             []uint32
	JoinAnsPayload     backend.JoinAnsPayload
	DeviceSession      storage.DeviceSession

	PRStartReqPayload *backend.PRStartReqPayload
	PRStartAnsPayload *backend.PRStartAnsPayload
}

var (
	netID       lorawan.NetID
	rx2DR       int
	rx1DROffset int
	rx1Delay    int
	keks        map[string][]byte
)

// Setup configures the package.
func Setup(conf config.Config) error {
	keks = make(map[string][]byte)

	netID = conf.NetworkServer.NetID
	rx2DR = conf.NetworkServer.NetworkSettings.RX2DR
	rx1DROffset = conf.NetworkServer.NetworkSettings.RX1DROffset
	rx1Delay = conf.NetworkServer.NetworkSettings.RX1Delay

	for _, k := range conf.JoinServer.KEK.Set {
		kek, err := hex.DecodeString(k.KEK)
		if err != nil {
			return errors.Wrap(err, "decode kek error")
		}

		keks[k.Label] = kek
	}

	return nil
}

// Handle handles a join-request
func Handle(ctx context.Context, rxPacket models.RXPacket) error {
	joinCtx := joinContext{
		ctx:      ctx,
		RXPacket: rxPacket,
	}

	return HandleRunTasks(&joinCtx)
}

func HandleRunTasks(joinCtx *joinContext) error {
	for _, f := range []func() error{
		joinCtx.setContextFromJoinRequestPHYPayload,
		joinCtx.getDeviceOrTryRoaming,
		joinCtx.getDeviceProfile,
		joinCtx.getServiceProfile,
		joinCtx.filterRxInfoByServiceProfile,
		joinCtx.logJoinRequestFramesCollected,
		joinCtx.abortOnDeviceIsDisabled,
		joinCtx.validateNonce,
		joinCtx.getRandomDevAddr,
		joinCtx.getJoinAcceptFromAS,
		joinCtx.sendUplinkMetaDataToNetworkController,
		joinCtx.flushDeviceQueue,
		joinCtx.createDeviceSession,
		joinCtx.createDeviceActivation,
		joinCtx.setDeviceMode,
		joinCtx.sendJoinAcceptDownlink,
	} {
		if err := f(); err != nil {
			if err == ErrAbort {
				return nil
			}

			return err
		}
	}

	return nil
}
